//==============================================================================
//
//         DTrueTypeFont - the true type font class in the ofc-library
//
//               Copyright (C) 2007  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2008-08-26 05:50:30 $ $Revision: 1.7 $
//
//==============================================================================

#include "ofc/DTrueTypeFont.h"

#ifdef HAVE_DTRUETYPEFONT
#include <ft2build.h>
#include FT_FREETYPE_H
#endif


#if _INTERFACE_

#include "ofc/config.h"

#include "ofc/DFont.h"

#if defined(HAVE_FREETYPE)
#define HAVE_DTRUETYPEFONT 1
#endif


#ifdef HAVE_DTRUETYPEFONT

//
// The DTrueTypeFont class implements methods for using a TrueType font. 
// Actually every font that can be loaded with the FreeType library, can 
// be used by this class.
// 
// @example
// #include <stdio.h>
// #include "ofc/DTrueTypeFont.h"
// 
// int main(int argc, char *argv[])
// {
//   DTrueTypeFont *font = [DTrueTypeFont new];
//   DFT_Glyph    glyph;
// 
//   unsigned     width,height;
//   char         name[] = "../test/arial.ttf";
// 
//   if ([font open :name])               // Open font file
//   {                                    // Print font info
//     printf("Family name:%s\n",    [font familyName]);
//     printf("Style name:%s\n",     [font styleName]);
//     printf("Glyphs in font:%d\n", [font glyphsInFont]);
// 
//     [font size :"a" :20 :15];          // Give the glyphs a size
// 
//     [font stringSize :"hello" :&width :&height]; // Get string dimensions
//     printf("String size of \"hello\":%u - %u\n", width, height);
// 
//     if ([font glyph :'a' :&glyph])     // Render a character to a glyph
//     {
//       unsigned xe = glyph.bitmapWidth -1;
//       unsigned ye = glyph.bitmapHeight - 1;
//       unsigned x,y;
//       
//       unsigned char *bitmap = glyph.bitmap;
//      
//       printf("Glyph bitmap:\n");       // Print the bitmap of the glyph
//       for (y = 0; y <= ye; y++)
//       {
//         unsigned char  mask = 0x80;
//         unsigned char *line = bitmap;
//         
//         for (x = 0; x <= xe; x++)
//         {
//           printf("%c", ((*line & mask) ? '1' : '0'));
// 
//           mask >>= 1;
//           if (mask == 0)
//           {
//             mask = 0x80;
//             line++;
//           }
//         }
//         bitmap += glyph.bitmapPitch;
//         printf("\n");
//       }
//     }
//     else
//       printf("The glyph 'a' is not present in the font.\n");
// 
//     [font close];
//   }
//   else
//     printf("Could not open font file \"%s\".\n", name);
// 
//   [font free];
// 
//   return 0;
// }
// 

@interface DTrueTypeFont : Object <DFont>
{
@private
  void     *_font;    // the loaded font
}

#endif
#endif




#ifdef HAVE_DTRUETYPEFONT

#if _PRIVATE_

// Private database

static FT_Library   _library = NULL;  // the freetype library
static int          _fonts   = 0;     // number of fonts classes

#endif


@implementation DTrueTypeFont

//// Constructors

//
// Initialise an graphic drawable object
//
// @return the object
//
- (DTrueTypeFont *) init
{
  [super init];

  if (_library == NULL)
  {
    _fonts = 0;
    if (FT_Init_FreeType(&_library))
    {
      _library = NULL;
      
      WARNING(DW_UNKNOWN_WARNING, "FT_Init_FreeType failed"); // ToDo beter error
    }
  }
  
  if (_library != NULL)
  {
    _fonts++;
  }
  
  _font = NULL;
  
  return self;
}


//// Copy related methods

//
// Do a shallow copy of the graphic drawable object
//
// @return the object
//
- shallowCopy
{
  WARNING(DW_METHOD_NOT_IMPL, "shallowCopy");
  
  return [super shallowCopy];
}


//// Deconstructor

//
// Free the truetype font
// 
// @return the object
// 
- free
{
  _fonts--;
  
  if (_fonts == 0)
  {
    FT_Done_FreeType(_library);

    _library = NULL;
  }

  
  return [super free];
}



//// Font members

//
// Get the family name of the font
//
// @return the family name (or NULL)
//
- (const char *) familyName
{
  FT_Face font = _font;
  
  return (font != NULL) ? font->family_name : NULL;
}

//
// Get the style name of the font
//
// @return the style name (or NULL)
//
- (const char *) styleName
{
  FT_Face font = _font;
  
  return (font != NULL) ? font->style_name : NULL;
}

//
// Get the number of characters in font
//
// @return the number of characters in the font (or -1)
//
- (int) glyphsInFont
{
  FT_Face font = _font;
  
  return (font != NULL) ? font->num_glyphs : -1;
}

#if 0
      fprintf(stderr, "Number of faces:       %ld.\n", font->num_faces);
      
      fprintf(stderr, "Number of characters:  %ld.\n", font->num_glyphs);
      fprintf(stderr, "Font flags:            %ld.\n", font->face_flags);
      fprintf(stderr, "Style flags:           %ld.\n", font->style_flags);
      fprintf(stderr, "Number of fixed sizes: %d.\n",  font->num_fixed_sizes);

      fprintf(stderr, "Ascender:              %d.\n",  font->ascender);
      fprintf(stderr, "Descender:             %d.\n",  font->descender);
      fprintf(stderr, "Height:                %d.\n",  font->height);
      fprintf(stderr, "Underline position     %d.\n",  font->underline_position);
#endif

//// Font methods

//
// Open a (true type) font file
//
// @param filename      the name of the font file
// 
// @return success
//
- (BOOL) open :(const char *) filename
{
  if ((filename == NULL) || (*filename == EOS))
  {
    WARNING(DW_INVALID_ARG, "filename");
  }
  else
  {
    FT_Face font;
    
    if (_font != NULL)
    {
      [self close];
    }
    
    if (FT_New_Face(_library, filename, 0, &font))
    {
      _font = NULL;
    }
    else
    {
      _font = font;
    }
  }
  
  return (_font != NULL);
}


//
// Check if the font is open
//
// @return is it ?
//
- (BOOL) isOpen
{
  return (_font != NULL);
}


//
// Close the font (file)
//
// @return success
//
- (BOOL) close
{
  if (_font != NULL)
  {
    FT_Done_Face(_font);
    
    _font = NULL;
  }
  
  return (_font == NULL);
}


//
// Set the font size based on the width and height of one character
// 
// @param width         the requested width
// @param height        the requested height
//
// @return success
//
- (BOOL) size :(unsigned) width :(unsigned) height
{
  BOOL ok = NO;
  
  if (_font == NULL)
  {
    WARNING(DW_INVALID_STATE, "open");
  }
  else if ((width == 0) || (height == 0))
  {
    WARNING(DW_INVALID_ARG, "widht/height");
  }
  else
  {
    ok = (FT_Set_Pixel_Sizes(_font, width, height) == 0);
  }
  
  return ok;
}

//
// Set the font size based on a string and requested a size
// 
// @param cstr          the string
// @param width         the requested width
// @param height        the requested height
//
// @return success
//

- (BOOL) size :(const char *) cstr :(unsigned) width :(unsigned) height
{
  BOOL ok = NO;
  
  if (_font == NULL)
  {
    WARNING(DW_INVALID_STATE, "open");
  }
  else if ((cstr == NULL) || (*cstr == EOS))
  {
    WARNING(DW_INVALID_ARG, "cstr");
  }
  else if ((width == 0) || (height == 0))
  {
    WARNING(DW_INVALID_ARG, "widht/height");
  }
  else
  {
    unsigned fwidth;
    unsigned fheight;
    
    FT_Set_Pixel_Sizes(_font, 25, 25);     // initialise size

    [self stringSize :cstr :&fwidth :&fheight];

    if ((fwidth > 0) && (width > 0))
      fwidth = (unsigned) (((long) width * 25L) / fwidth);

    if ((fheight > 0) && (height > 0))
      fheight = (unsigned) (((long) height * 25L) / fheight);

    if (width == 0)
      fwidth = fheight;

    if (height == 0)
      fheight = fwidth;

    if (fwidth < 1)
      fwidth = 1;

    if (fheight < 1)
      fheight = 1;

    ok = (FT_Set_Pixel_Sizes(_font, fwidth, fheight) == 0);
  }
  
  return ok;
}

//
// Get the size of a string for the current font size
// 
// @param cstr          the string
// @param width         the width of the string
// @param height        the height of the string
//
// @return success
//
- (BOOL) stringSize :(const char *) cstr :(unsigned *) width :(unsigned *) height
{
  BOOL ok = NO;
  
  if (_font == NULL)
  {
    WARNING(DW_INVALID_STATE, "open");
  }
  else
  {
    *width  = 0;
    *height = 0;
  
    while (*cstr != '\0')
    {
      FT_Face font = _font;
      
      ok = (FT_Load_Char(_font, *cstr++, FT_LOAD_RENDER|FT_LOAD_MONOCHROME) != 0);
      
      if ((font->size->metrics.height >> 6) > *height)
      {
        *height = font->size->metrics.height >> 6;
      }
      
      (*width) += font->glyph->advance.x >> 6;
    }
  }
  
  return ok;
}

//
// Get the size of a character for the current font and size
// 
// @param ch            the character
// @param width         the width of the string
// @param height        the height of the string
//
// @return success
//
- (BOOL) charSize :(char) ch :(unsigned *) width :(unsigned *) height
{
  BOOL ok = NO;
  
  if (_font == NULL)
  {
    WARNING(DW_INVALID_STATE, "open");
  }
  else
  {
    FT_Face font = _font;
    
    *width  = 0;
    *height = 0;
  
    ok = (FT_Load_Char(font, ch, FT_LOAD_RENDER|FT_LOAD_MONOCHROME) == 0);
      
    *height = font->size->metrics.height >> 6;
    *width  = font->glyph->advance.x >> 6;
  }
  
  return ok;
}

//
// Get the glyph info for blitting a character for the current font and size
// 
// @param ch            the character
// @param glyph         the character bitmap and blit info
//
// @return success
//
- (BOOL) glyph :(char) ch :(DFT_Glyph *) glyph
{
  BOOL ok = NO;
  
  if (_font == NULL)
  {
    WARNING(DW_INVALID_STATE, "open");
  }
  else if (glyph == NULL)
  {
    WARNING(DW_INVALID_ARG, "glyph");
  }
  else
  {
    FT_Face font = _font;
    
    ok = (FT_Load_Char(font, ch, FT_LOAD_RENDER|FT_LOAD_MONOCHROME) == 0);

    glyph->offsetX      = font->glyph->bitmap_left;
    glyph->offsetY      = font->glyph->bitmap_top;
    glyph->advanceX     = font->glyph->advance.x >> 6;
    glyph->advanceY     = font->glyph->advance.y >> 6;

    glyph->ascender     = font->size->metrics.ascender >> 6;
    glyph->descender    = font->size->metrics.descender >> 6;
    glyph->height       = font->size->metrics.height >> 6;

    glyph->bitmap       = font->glyph->bitmap.buffer;
    glyph->bitmapHeight = font->glyph->bitmap.rows;
    glyph->bitmapWidth  = font->glyph->bitmap.width;
    glyph->bitmapPitch  = font->glyph->bitmap.pitch;
  }
  
  return ok;
}

@end

#endif

/*===========================================================================*/
